import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:macless_haystack/accessory/accessory_model.dart';
//import 'package:maps_launcher/maps_launcher.dart';
import 'package:map_launcher/map_launcher.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:latlong2/latlong.dart';
import 'package:macless_haystack/accessory/accessory_list_item.dart';
import 'package:macless_haystack/accessory/accessory_list_item_placeholder.dart';
import 'package:macless_haystack/accessory/accessory_registry.dart';
import 'package:macless_haystack/accessory/no_accessories.dart';
import 'package:macless_haystack/history/accessory_history.dart';
import 'package:macless_haystack/location/location_model.dart';

import '../callbacks.dart';
import 'package:scrollview_observer/scrollview_observer.dart';
import 'package:visibility_detector/visibility_detector.dart';

class AccessoryList extends StatefulWidget {
  final LoadLocationUpdatesCallback loadLocationUpdates;
  final AsyncCallback loadVisibleItemsLocationUpdates;
  final AsyncValueSetter<Accessory> loadOneLocationUpdates;
  final void Function(LatLng point)? centerOnPoint;

  /// Display a location overview all accessories in a concise list form.
  ///
  /// For each accessory the name and last known locaiton information is shown.
  /// Uses the accessories in the [AccessoryRegistry].
  const AccessoryList({
    super.key,
    required this.loadLocationUpdates,
    required this.loadVisibleItemsLocationUpdates,
    required this.loadOneLocationUpdates,
    this.centerOnPoint,
  });

  @override
  State<StatefulWidget> createState() {
    return _AccessoryListState();
  }
}

class _AccessoryListState extends State<AccessoryList>
    with SingleTickerProviderStateMixin {
  final ScrollController _scrollController = ScrollController();
  late ListObserverController observerController;

  late StreamSubscription<JumpToListItemEvent> jumpToListener;

  late AnimationController _blinkController;
  String _blinkAccessoryId = '';

  @override
  void initState() {
    super.initState();

    _blinkController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 300),
    )..repeat(reverse: true, period: Duration(milliseconds: 600));

    observerController = ListObserverController(controller: _scrollController);

    var accessoryRegistry =
        Provider.of<AccessoryRegistry>(context, listen: false);

    jumpToListener =
        accessoryRegistry.eventBus.on<JumpToListItemEvent>().listen((event) {
      if (event.tabIdx == 0) {
        observerController.jumpTo(index: event.itemIdx);
        setState(() {
          _blinkAccessoryId = accessoryRegistry.accessories[event.itemIdx].id;
        });
        Future.delayed(Duration(milliseconds: 1200), () {
          setState(() {
            _blinkAccessoryId = '';
          });
        });
      }
    });
  }

  openMapsSheet(context, LatLng location, String title) async {
    try {
      final coords = Coords(location.latitude, location.longitude);
      final availableMaps = await MapLauncher.installedMaps;

      showModalBottomSheet(
        context: context,
        builder: (BuildContext context) {
          return SafeArea(
            child: SingleChildScrollView(
              child: Container(
                child: Wrap(
                  children: <Widget>[
                    for (var map in availableMaps)
                      ListTile(
                        onTap: () => map.showMarker(
                          coords: coords,
                          title: title,
                        ),
                        title: Text(map.mapName),
                        leading: SvgPicture.asset(
                          map.icon,
                          height: 30.0,
                          width: 30.0,
                        ),
                      ),
                  ],
                ),
              ),
            ),
          );
        },
      );
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Consumer2<AccessoryRegistry, LocationModel>(
      builder: (context, accessoryRegistry, locationModel, child) {
        var accessories = accessoryRegistry.accessories;

        // Show placeholder while accessories are loading
        if (accessoryRegistry.loading) {
          return LayoutBuilder(builder: (context, constraints) {
            // Show as many accessory placeholder fitting into the vertical space.
            // Minimum one, maximum 6 placeholders
            var nrOfEntries =
                min(max((constraints.maxHeight / 64).floor(), 1), 6);
            List<Widget> placeholderList = [];
            for (int i = 0; i < nrOfEntries; i++) {
              placeholderList.add(const AccessoryListItemPlaceholder());
            }
            return Scrollbar(
              child: ListView(
                children: placeholderList,
              ),
            );
          });
        }

        if (accessories.isEmpty) {
          return const NoAccessoriesPlaceholder();
        }
        // Use pull to refresh method
        return SlidableAutoCloseBehavior(
          child: RefreshIndicator(
            /* onRefresh: () async {
              await widget.loadLocationUpdates(null);
            }, */
            onRefresh: widget.loadVisibleItemsLocationUpdates,
            child: Scrollbar(
              child: ListViewObserver(
                controller: observerController,
                child: ListView(
                  controller: _scrollController,
                  children: accessories.map((accessory) {
                    // Calculate distance from users devices location
                    Widget? trailing;
                    if (locationModel.here != null &&
                        accessory.lastLocation != null) {
                      const Distance distance = Distance();
                      final double km = distance.as(
                          LengthUnit.Kilometer,
                          locationModel.wgs84ToGcj02(locationModel.here!)!,
                          locationModel.wgs84ToGcj02(accessory.lastLocation!)!);
                      trailing = Text('$km km');
                    }
                    // Get human readable location
                    return Slidable(
                      key: ValueKey(accessory),
                      startActionPane: !accessory.isActive
                          ? null
                          : ActionPane(
                              key: ValueKey(accessory),
                              motion: const ScrollMotion(),
                              dragDismissible: false,
                              children: [
                                  SlidableAction(
                                    onPressed: (context) {
                                      if (!accessory.isLoadingReports) {
                                        widget
                                            .loadOneLocationUpdates(accessory);
                                      }
                                    },
                                    foregroundColor:
                                        Theme.of(context).primaryColor,
                                    icon: Icons.refresh,
                                    label: '刷新位置',
                                  ),
                                ]),
                      endActionPane: ActionPane(
                        motion: const DrawerMotion(),
                        children: [
                          if (accessory.isActive)
                            SlidableAction(
                              onPressed: (context) async {
                                if (accessory.lastLocation != null &&
                                    accessory.isActive) {
                                  var loc = locationModel
                                      .wgs84ToGcj02(accessory.lastLocation!)!;
                                  openMapsSheet(context, loc, accessory.name);
                                }
                              },
                              foregroundColor: Theme.of(context).primaryColor,
                              icon: Icons.directions,
                              label: '导航',
                            ),
                          if (accessory.isActive)
                            SlidableAction(
                              onPressed: (context) {
                                Navigator.push(
                                  context,
                                  MaterialPageRoute(
                                      builder: (context) => AccessoryHistory(
                                            accessory: accessory,
                                          )),
                                );
                              },
                              backgroundColor: Theme.of(context).primaryColor,
                              icon: Icons.history,
                              label: '历史',
                            ),
                          if (!accessory.isActive)
                            SlidableAction(
                              onPressed: (context) {
                                var accessoryRegistry =
                                    Provider.of<AccessoryRegistry>(context,
                                        listen: false);
                                var newAccessory = accessory.clone();
                                newAccessory.isActive = true;
                                accessoryRegistry.editAccessory(
                                    accessory, newAccessory);
                              },
                              backgroundColor: Colors.green,
                              foregroundColor: Colors.white,
                              icon: Icons.toggle_on_outlined,
                              label: '激活',
                            ),
                        ],
                      ),
                      child: Builder(builder: (context) {
                        return VisibilityDetector(
                            key: Key('item_${accessory.id}'),
                            onVisibilityChanged: (visibilityInfo) {
                              final visiblePercentage =
                                  visibilityInfo.visibleFraction;
                              if (visiblePercentage > 0) {
                                accessoryRegistry.visibleAccessories
                                    .add(accessory);
                              } else {
                                accessoryRegistry.visibleAccessories
                                    .remove(accessory);
                              }
                              if (accessory.isActive &&
                                  visiblePercentage > 0 &&
                                  mounted &&
                                  !accessory.isLoadingReports &&
                                  !accessoryRegistry.loadedAccessoryIds
                                      .contains(accessory.id)) {
                                accessory.isLoadingReports = true;
                                accessoryRegistry.asyncQueue.add(() {
                                  if (mounted &&
                                      accessoryRegistry.visibleAccessories
                                          .contains(accessory) &&
                                      !accessoryRegistry.loadedAccessoryIds
                                          .contains(accessory.id)) {
                                    accessoryRegistry.loadedAccessoryIds
                                        .add(accessory.id);
                                    return widget
                                        .loadOneLocationUpdates(accessory);
                                  } else {
                                    accessory.isLoadingReports = false;
                                  }

                                  return Future.value();
                                });
                              }
                            },
                            child: AnimatedBuilder(
                              animation: _blinkController,
                              builder: (context, child) {
                                return Opacity(
                                  opacity: accessory.id == _blinkAccessoryId
                                      ? _blinkController.value
                                      : 1.0,
                                  child: child,
                                );
                              },
                              child: AccessoryListItem(
                                accessory: accessory,
                                distance: trailing,
                                herePlace: locationModel.herePlace,
                                onTap: () {
                                  if (accessory.isActive) {
                                    var lastLocation = accessory.lastLocation;
                                    if (lastLocation != null) {
                                      widget.centerOnPoint?.call(locationModel
                                          .wgs84ToGcj02(lastLocation)!);
                                    }
                                  }
                                },
                                onLongPress: !accessory.isActive
                                    ? null
                                    : () {
                                        if (!accessory.isLoadingReports) {
                                          widget.loadOneLocationUpdates(
                                              accessory);
                                        }
                                      },
                              ),
                            ));
                      }),
                    );
                  }).toList(),
                ),
              ),
            ),
          ),
        );
      },
    );
  }

  @override
  void dispose() {
    jumpToListener.cancel();
    _blinkController.dispose();
    observerController.controller?.dispose();
    super.dispose();
  }
}
